package com.passion.fs.util;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.SecureRandom;

/**
 * 类描述：文件加/解密工具类
 *
 * @author ZhuYin
 * @since 2025年01月09日
 */
public class FileEncDecUtil {

    private static final String ALGORITHM = "AES";
    /** AES supports key sizes of 128, 192 and 256 bits */
    private static final int KEY_SIZE = 256;

    /**
     * 生成一个SecretKey的密钥，可以用这个密钥获取到 SecretKey 对象
     */
    private static SecretKey generateKey() throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
        keyGen.init(KEY_SIZE, new SecureRandom());
        return keyGen.generateKey();
    }

    public static String getSecretKey() throws Exception {
        return saveKey(generateKey());
    }

    /**
     * 使用 Base64加密串 给文件加密
     *
     * @param secretKeyStr    Base64加密串
     * @param inputFilePath   输入文件全路径
     * @param outputFilePath  输出文件全路径
     * @author ZhuYin
     * @since 2025年01月09日
     */
    public static void encrypt(String secretKeyStr, String inputFilePath, String outputFilePath) throws Exception {
        SecretKey key = loadKey(secretKeyStr);
        doCrypto(Cipher.ENCRYPT_MODE, key, inputFilePath, outputFilePath);
    }

    /**
     * 使用 Base64加密串 给文件加密
     *
     * @param secretKeyStr  Base64加密串
     * @param inputFile     输入文件对象
     * @param outputFile    输出文件对象
     * @author ZhuYin
     * @since 2025年01月09日
     */
    public static void encrypt(String secretKeyStr, File inputFile, File outputFile) throws Exception {
        SecretKey key = loadKey(secretKeyStr);
        doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
    }

    /**
     * 使用 SecretKey 给文件加密
     *
     * @param key             SecretKey对象
     * @param inputFilePath   输入文件全路径
     * @param outputFilePath  输出文件全路径
     * @author ZhuYin
     * @since 2025年01月09日
     */
    private static void encrypt(SecretKey key, String inputFilePath, String outputFilePath) throws Exception {
        doCrypto(Cipher.ENCRYPT_MODE, key, inputFilePath, outputFilePath);
    }

    /**
     * 使用 SecretKey 给文件加密
     *
     * @param key           SecretKey对象
     * @param inputFile     输入文件对象
     * @param outputFile    输出文件对象
     * @author ZhuYin
     * @since 2025年01月09日
     */
    private static void encrypt(SecretKey key, File inputFile, File outputFile) throws Exception {
        doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
    }

    /**
     * 使用 Base64加密串 给文件解密
     *
     * @param secretKeyStr Base64加密串
     * @param inputFile    输入文件全路径
     * @param outputFile   输出文件全路径
     * @author ZhuYin
     * @since 2025年01月09日
     */
    public static void decrypt(String secretKeyStr, String inputFile, String outputFile) throws Exception {
        SecretKey key = loadKey(secretKeyStr);
        doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
    }

    /**
     * 使用 Base64加密串 给文件解密
     *
     * @param secretKeyStr  Base64加密串
     * @param inputFile     输入文件对象
     * @param outputFile    输出文件对象
     * @author ZhuYin
     * @since 2025年01月09日
     */
    public static void decrypt(String secretKeyStr, File inputFile, File outputFile) throws Exception {
        SecretKey key = loadKey(secretKeyStr);
        doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
    }

    /**
     * 使用 SecretKey 给文件解密
     *
     * @param key         SecretKey对象
     * @param inputFile   输入文件全路径
     * @param outputFile  输出文件全路径
     * @author ZhuYin
     * @since 2025年01月09日
     */
    private static void decrypt(SecretKey key, String inputFile, String outputFile) throws Exception {
        doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
    }

    /**
     * 使用 SecretKey 给文件解密
     *
     * @param key           SecretKey对象
     * @param inputFile     输入文件对象
     * @param outputFile    输出文件对象
     * @author ZhuYin
     * @since 2025年01月09日
     */
    private static void decrypt(SecretKey key, File inputFile, File outputFile) throws Exception {
        doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
    }

    /**
     * 使用 SecretKey 给文件加/解密
     *
     * @param cipherMode    加密模式
     * @param key           SecretKey对象
     * @param inputFile     输入文件对象
     * @param outputFile    输出文件对象
     * @author ZhuYin
     * @since 2025年01月09日
     */

    private static void doCrypto(int cipherMode, SecretKey key, File inputFile, File outputFile) throws Exception {
        doCrypto(cipherMode, key, inputFile.getAbsolutePath(), outputFile.getAbsolutePath());
    }

    /**
     * 方法说明：
     *
     * @param cipherMode  加密模式
     * @param key         SecretKey对象
     * @param inputFile   输入文件全路径
     * @param outputFile  输出文件全路径
     * @author ZhuYin
     * @since 2025年01月09日
     */
    private static void doCrypto(int cipherMode, SecretKey key, String inputFile, String outputFile) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(cipherMode, key);
        try (FileInputStream inputStream = new FileInputStream(inputFile);
             FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            byte[] buffer = new byte[64];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                byte[] output = cipher.update(buffer, 0, bytesRead);
                if (output != null) {
                    outputStream.write(output);
                }
            }
            byte[] outputBytes = cipher.doFinal();
            if (outputBytes != null) {
                outputStream.write(outputBytes);
            }
        } catch (IOException ex) {
            throw new IOException("Error reading or writing file: " + ex.getMessage(), ex);
        }
    }


    /**
     * 通过 SecretKey 的密钥来获取 SecretKey 对象
     *
     * @param base64Key Base64编码的密钥
     * @return 返回 SecretKey 对象
     * @author ZhuYin
     * @since 2025年01月09日
     */
    private static SecretKey loadKey(String base64Key) {
        byte[] decodedKey = java.util.Base64.getDecoder().decode(base64Key);
        return new SecretKeySpec(decodedKey, 0, decodedKey.length, ALGORITHM);
    }

    /**
     * 将 SecretKey 对象转成一个密钥串
     *
     * @param key SecretKey对象
     * @return 返回一个Base64的密钥串
     * @author ZhuYin
     * @since 2025年01月09日
     */
    private static String saveKey(SecretKey key) {
        return java.util.Base64.getEncoder().encodeToString(key.getEncoded());
    }

    // public static void main(String[] args) throws Exception {
    //     // Generate a key
    //     SecretKey key1 = FileEncDecUtil.generateKey();
    //     String safeKey = FileEncDecUtil.saveKey(key1);
    //     System.out.println("Key: " + safeKey);
    //     SecretKey key2 = loadKey(safeKey);
    //     System.out.println("Key: " + key2);
    //     System.out.println(Objects.equals(key1, key2));
    //
    //     // Define file paths
    //     String originalFile = "C:\\Users\\Administrator\\Downloads\\接口平台使用说明.doc";
    //     String encryptedFile = "C:\\Users\\Administrator\\Desktop\\aaa.doc";
    //     String decryptedFile = "C:\\Users\\Administrator\\Desktop\\bbb.doc";
    //
    //     String secretKey = getSecretKey();
    //
    //     // Encrypt the file
    //     FileEncDecUtil.encrypt(secretKey, originalFile, encryptedFile);
    //
    //     // Decrypt the file
    //     FileEncDecUtil.decrypt(secretKey, encryptedFile, decryptedFile);
    //
    //     String originMd5 = FileUtil.fileMd5(new File(originalFile));
    //     String decryptedMd5 = FileUtil.fileMd5(new File(decryptedFile));
    //     System.out.println(originMd5);
    //     System.out.println(decryptedMd5);
    //     System.out.println(Objects.equals(originMd5, decryptedMd5));
    // }
}